import type { ReactNode } from 'react';
import React, {
  forwardRef,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from 'react';

const classPrefix = 'bbt-textarea';

export type TextAreaProps = Pick<
  React.DetailedHTMLProps<
    React.TextareaHTMLAttributes<HTMLTextAreaElement>,
    HTMLTextAreaElement
  >,
  | 'autoComplete'
  | 'autoFocus'
  | 'disabled'
  | 'readOnly'
  | 'name'
  | 'onFocus'
  | 'onBlur'
  | 'onCompositionStart'
  | 'onCompositionEnd'
  | 'onClick'
> & {
  style?: React.CSSProperties & Partial<Record<string | never, string>>;
  onChange?: (val: string) => void;
  value?: string;
  defaultValue?: string;
  placeholder?: string;
  rows?: number;
  maxLength?: number;
  showCount?: boolean | ((length: number, maxLength?: number) => ReactNode);
  autoSize?:
    | boolean
    | {
        minRows?: number;
        maxRows?: number;
      };
  id?: string;
};

export type TextAreaRef = {
  clear: () => void;
  focus: () => void;
  blur: () => void;
  nativeElement: HTMLTextAreaElement | null;
};

const defaultProps = {
  rows: 2,
  showCount: false as NonNullable<TextAreaProps['showCount']>,
  autoSize: false as NonNullable<TextAreaProps['autoSize']>,
  defaultValue: '',
};

export const TextArea = forwardRef<TextAreaRef, TextAreaProps>(
  (p: TextAreaProps, ref) => {
    const props = { ...defaultProps, ...(p || {}) };
    const { autoSize, showCount, maxLength } = props;
    const [value, setValue] = useState(props?.defaultValue || '');
    if (props.value === null) {
      console.error(
        'TextArea',
        '`value` prop on `TextArea` should not be `null`. Consider using an empty string to clear the component.',
      );
    }
    const nativeTextAreaRef = useRef<HTMLTextAreaElement>(null);

    useImperativeHandle(ref, () => ({
      clear: () => {
        setValue('');
      },
      focus: () => {
        nativeTextAreaRef.current?.focus();
      },
      blur: () => {
        nativeTextAreaRef.current?.blur();
      },
      get nativeElement() {
        return nativeTextAreaRef.current;
      },
    }));

    useEffect(() => {
      if (!autoSize) return;
      const textArea = nativeTextAreaRef.current;
      if (!textArea) return;
      textArea.style.height = 'auto';
      let height = textArea.scrollHeight;
      if (typeof autoSize === 'object') {
        const computedStyle = window.getComputedStyle(textArea);
        const lineHeight = parseFloat(computedStyle.lineHeight);
        if (autoSize.minRows) {
          height = Math.max(height, autoSize.minRows * lineHeight);
        }
        if (autoSize.maxRows) {
          height = Math.min(height, autoSize.maxRows * lineHeight);
        }
      }
      textArea.style.height = `${height}px`;
    }, [value, autoSize]);

    const compositingRef = useRef(false);

    let count;
    const valueLength = value.length;
    if (typeof showCount === 'function') {
      count = showCount(valueLength, maxLength);
    } else if (showCount) {
      count = (
        <div className={`${classPrefix}-count`}>
          {maxLength === undefined
            ? valueLength
            : valueLength + '/' + maxLength}
        </div>
      );
    }

    return (
      <div className={classPrefix}>
        <textarea
          ref={nativeTextAreaRef}
          className={`${classPrefix}-element`}
          rows={props.rows}
          value={value}
          placeholder={props.placeholder}
          onChange={(e) => {
            let v = e.target.value;
            if (maxLength && !compositingRef.current) {
              v = v.slice(0, maxLength);
            }
            setValue(v);
          }}
          id={props.id}
          onCompositionStart={(e) => {
            compositingRef.current = true;
            props.onCompositionStart?.(e);
          }}
          onCompositionEnd={(e) => {
            compositingRef.current = false;
            if (maxLength) {
              const v = (e.target as HTMLTextAreaElement).value;
              setValue(v.slice(0, maxLength));
            }
            props.onCompositionEnd?.(e);
          }}
          autoComplete={props.autoComplete}
          autoFocus={props.autoFocus}
          disabled={props.disabled}
          readOnly={props.readOnly}
          id={props.name}
          onFocus={props.onFocus}
          onBlur={props.onBlur}
          onClick={props.onClick}
        />
        {count}
      </div>
    );
  },
);

TextArea.defaultProps = defaultProps;
